import { ConstantEnum } from '@libs/common/enums';
import {
  BaseRepository,
  indexPaginateBuilder,
  showBuilderEntity,
} from '@libs/common/repository';
import { Inject, Injectable } from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
import { DataSource } from 'typeorm';

import { DepartmentConfigService } from '@/dingding/department-config/department-config.service';

import {
  CountOvertimeApplyDto,
  CreateOvertimeApplyDto,
  IndexOvertimeApplyDto,
  ShowOvertimeApplyDto,
  UpdateOvertimeApplyDto,
  UpdateStatusOvertimeApplyDto,
} from './dto/overtime-apply.dto';
import {
  OvertimeApply,
  OvertimeApplyStatus,
} from './entities/overtime-apply.entity';

export interface UserCountVo {
  dingId: string;
  userName: string;
  /** 单位小时 */
  totalTime: number;
}

@Injectable()
export class OvertimeApplyService extends BaseRepository {
  constructor(
    @Inject(ConstantEnum.DB_DATA_SOURCE_MANAGER) dataSource: DataSource,
  ) {
    super(dataSource);
  }

  protected repository = this.getRepository(OvertimeApply);

  protected repositoryManager = () => this.getRepository(OvertimeApply);

  @Inject(DepartmentConfigService)
  private departmentConfigService: DepartmentConfigService;

  async index(dto: IndexOvertimeApplyDto) {
    const res = await indexPaginateBuilder({
      dto,
      repository: this.repository,
    });
    return res;
  }

  show<D extends ShowOvertimeApplyDto = ShowOvertimeApplyDto>(dto: D) {
    return showBuilderEntity(this.repository, dto);
  }

  update(dto: UpdateOvertimeApplyDto | UpdateOvertimeApplyDto[]) {
    const dtos = Array.isArray(dto) ? dto : [dto];
    const entities = plainToInstance(OvertimeApply, dtos);

    return this.repositoryManager().save(entities);
  }

  async create(dto: CreateOvertimeApplyDto) {
    const instance = plainToInstance(CreateOvertimeApplyDto, dto);

    const errors = await validate(instance);
    console.log('🚀 ~ OvertimeApplyService ~ create ~ errors:', errors);

    if (errors.length > 0) {
      const meg = Object.values(errors[0]?.constraints || {})[0];
      throw new Error(meg || '参数错误');
    }

    const entity = this.repositoryManager().create(instance);
    await this.repositoryManager().insert(entity);

    return entity;
  }

  /** 通过卡片实例 id 更新状态 */
  async updateStatusByOutTrackId(dto: UpdateStatusOvertimeApplyDto) {
    const instance = plainToInstance(UpdateStatusOvertimeApplyDto, dto);

    const errors = await validate(instance);

    if (errors.length > 0) {
      const meg = Object.values(errors[0]?.constraints || {})[0];
      throw new Error(meg || '参数错误');
    }

    await this.repositoryManager().update(
      { outTrackId: dto.outTrackId },
      { status: dto.status },
    );
  }

  /** 通过卡片实例 id 查询记录 */
  findByOutTrackId(outTrackId: string) {
    if (!outTrackId) throw new Error('参数错误');

    return this.repository.findOneBy({ outTrackId: outTrackId });
  }

  findById(id: string) {
    return this.repository.findOneBy({ id });
  }

  import() {
    // CODE
  }

  export() {
    // CODE
  }

  userCount(user: SC.User.Info) {
    return this.count({ dingId: user.dingId });
  }

  async count(dto: CountOvertimeApplyDto) {
    const queryBuilder = this.repository
      .createQueryBuilder('o')
      .select('o.dingId', 'dingId')
      .addSelect('CAST(SUM(o.time) AS UNSIGNED)', 'totalTime')
      .where('o.status IN (:...statuses)', {
        statuses: [OvertimeApplyStatus.待审核, OvertimeApplyStatus.通过],
      });

    if (dto.dingId) {
      queryBuilder
        .andWhere('o.dingId = :dingId', { dingId: dto.dingId })
        .groupBy('o.dingId');
    } else if (dto.userId) {
      queryBuilder
        .andWhere('o.userId = :userId', { userId: dto.userId })
        .groupBy('o.userId');
    }

    const result = await queryBuilder.getRawOne<UserCountVo>();

    if (result?.totalTime) result.totalTime = Number(result.totalTime);

    return result;
  }

  async countTime(dto: CountOvertimeApplyDto) {
    return (await this.count(dto))?.totalTime || 0;
  }
}
